#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/ledc.h"
#include "esp_check.h"
#include "esp_log.h"
#include "led_hal.h"

static const char *TAG = "led_hal";

/* Warning:
 * For ESP32, ESP32S2, ESP32S3, ESP32C3, ESP32C2, ESP32C6, ESP32H2, ESP32P4 targets,
 * when LEDC_DUTY_RES selects the maximum duty resolution (i.e. value equal to SOC_LEDC_TIMER_BIT_WIDTH),
 * 100% duty cycle is not reachable (duty cannot be set to (2 ** SOC_LEDC_TIMER_BIT_WIDTH)).
 */

/**
 * @brief ledc_init
 *
 * @param gpio lcd brightness IO
 * @return esp_err_t
 */
esp_err_t ledc_init(uint8_t gpio)
{
    esp_err_t error;
    // Prepare and then apply the LEDC PWM timer configuration
    ledc_timer_config_t ledc_timer = {
        .speed_mode = LEDC_MODE,
        .duty_resolution = LEDC_DUTY_RES,
        .timer_num = LEDC_TIMER,
        .freq_hz = LEDC_FREQUENCY, // Set output frequency at 8 kHz
        .clk_cfg = LEDC_AUTO_CLK};
    error = ledc_timer_config(&ledc_timer);
    ESP_RETURN_ON_ERROR(error, TAG, "ledc timer config error!");

    // Prepare and then apply the LEDC PWM channel configuration
    ledc_channel_config_t ledc_channel = {
        .speed_mode = LEDC_MODE,
        .channel = LEDC_CHANNEL,
        .timer_sel = LEDC_TIMER,
        .intr_type = LEDC_INTR_DISABLE,
        .gpio_num = gpio,
        .duty = 0, // Set duty to 0%
        .hpoint = 0};
    error = ledc_channel_config(&ledc_channel);
    ESP_RETURN_ON_ERROR(error, TAG, "ledc channel config error!");

    error = ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, 0); // init duty is 0%
    ESP_RETURN_ON_ERROR(error, TAG, "ledc init duty error!");

    ESP_LOGI(TAG, "Ledc init successfully");
    return ESP_OK;
}

/**
 * @brief ledc_set_brightness
 *
 * @param brightness 0-100%
 * @return esp_err_t
 */
esp_err_t ledc_set_brightness(uint32_t brightness)
{
    esp_err_t error;
    /* limit brightness start */
    if (brightness < 5)
    {
        brightness = 5;
    }
    /* limit brightness end */
    uint16_t ledc_duty = brightness * (81);                     /* (0-8192) -> (0%-100%) */
    error = ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, ledc_duty);  /* Set duty to 50% */
    ESP_RETURN_ON_ERROR(error, TAG, "ledc set duty error!");    /* check err */
    error = ledc_update_duty(LEDC_MODE, LEDC_CHANNEL);          /* Update duty to apply the new value */
    ESP_RETURN_ON_ERROR(error, TAG, "ledc update duty error!"); /* check err */

    return ESP_OK;
}

/**
 * @brief ledc_app_test
 *
 */
void ledc_app_test(void)
{
    ledc_init(48);
    for (int i = 0; i < 100; i++)
    {
        ledc_set_brightness(i);
        vTaskDelay(pdMS_TO_TICKS(20));
    }
    for (int i = 100; i > 0; i--)
    {
        ledc_set_brightness(i);
        vTaskDelay(pdMS_TO_TICKS(20));
    }
}